/*
 *  Copyright 2021 (c) Microsoft Corporation.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*
 * MuxLogger.h
 *
 *  Created on: Oct 4, 2020
 *      Author: Tamer Ahmed
 */

#ifndef MUXLOGGER_H_
#define MUXLOGGER_H_

#include <map>
#include <memory>

#include <boost/format.hpp>
#include <boost/log/exceptions.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sinks/syslog_backend.hpp>

#include "swss/logger.h"

namespace test {
class MuxLoggerTest;
}

namespace common
{
class MuxLogger;
using MuxLoggerPtr = std::shared_ptr<MuxLogger>;

/**
 *@class MuxLoggerExceptionHandler
 *
 *@brief Handles exception generated by boost logging framework
 */
class MuxLoggerExceptionHandler
{
public:
    /**
    *@method operator()
    *
    *@brief handles runtime error exceptions
    *
    *@param ex (in)  runtime error exception
    *
    *@throw MUX logger exception corresponding to exception received
    */
    void operator()(const boost::log::runtime_error &ex) const;


    /**
    *@method operator()
    *
    *@brief handles std exceptions
    *
    *@param ex (in)  std exception
    *
    *@throw MUX logger exception corresponding to exception received
    */
    void operator()(const std::exception &ex) const;
};

/**
 *@class MuxLogger
 *
 *@brief MUX Logger class wraps boost logging framework. It is a singleton
 *       class that manages MUX Logging
 */
class MuxLogger
{
public:
    /**
    *@method MuxLogger
    *
    *@brief class copy constructor
    *
    *@param muxLogger (in)  reference to muxLogger object to be copied
    */
    MuxLogger(const MuxLogger &) = delete;

    /**
    *@method ~MuxLogger
    *
    *@brief class destructor
    *
    */
    virtual ~MuxLogger() = default;

    /**
    *@method getInstance
    *
    *@brief constructs MuxLogger singleton instance
    *
    *@return shared pointer to MuxLogger singleton instance
    */
    static MuxLoggerPtr getInstance();

    /**
    *@method initialize
    *
    *@brief initialize MUX logging class
    *
    *@param prog (in)               program name to be used when logging
    *@param path (in)               path on file system to MUX logging file
    *@param level (in)              minimum logging severity level
    *@param extraLogFile (in)       save log in an extra log file
    *@param linkToSwssLogger (in)   output log to swss logger
    *
    *@return none
    */
    void initialize(std::string &prog, std::string &path, boost::log::trivial::severity_level level, bool extraLogFile, bool linkToSwsslogger=false);

    /**
    *@method setLevel
    *
    *@brief setter for logging severity level
    *
    *@param level (in)  severity level for log messages that would follow
    *
    *@return none
    */
    void setLevel(const boost::log::trivial::severity_level level);

    /**
    *@method getLevel
    *
    *@brief getter for logging severity level
    *
    *@return current logging severity level
    */
    boost::log::trivial::severity_level getLevel() const {return mLevel;};

    /**
    *@method getLogger
    *
    *@brief getter for Severity Logger
    *
    *@return severity logger instance
    */
    boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>&
    getLogger() {return mSeverityLogger;};

    /**
    *@method startSwssLogger
    *
    *@brief start swss logger
    *
    *@return None
    */
    virtual void startSwssLogger(const std::string &swssPrio);

    /**
     *@method swssPrioNotify
     *
     *@brief process syslog priority setting from swssloglevel
     *
     *@param component (in)    program name
     *@param prioStr (in)      syslog log level string
     *
     *@return None
     */
    void swssPrioNotify(std::string component, std::string prioStr);

    /**
     *@method swssOutputNotify
     *
     *@brief process syslog output setting from swssloglevel, only support syslog
     *
     *@param component (in)     program name
     *@param outputStr (in)     syslog log output destination
     *
     *@return None
     */
    void swssOutputNotify(std::string component, std::string outputStr);

    /**
    *@method isLinkToSwssLogger
    *
    *@brief return if mux logger is linked to swss logger
    *
    *@return true if swss logger is linked
    */
    bool isLinkToSwssLogger() { return mLinkToSwssLogger; };

private:
    friend class std::shared_ptr<MuxLogger>;
    friend class test::MuxLoggerTest;
    friend MuxLoggerPtr std::make_shared<MuxLogger> ();

    /**
    *@method MuxLogger
    *
    *@brief class constructor
    *
    */
    MuxLogger() = default;

    /**
    *@method addExtraLogFileSink
    *
    *@brief Add an extra log file sink
    *
    *@return none
    */
    void addExtraLogFileSink(std::string &prog, const std::string &logFile);

    /**
    *@method addSyslogSink
    *
    *@brief add boost syslog sink to the logger
    *
    *@return none
    */
    void addSyslogSink(std::string &prog);

    /**
    *@method addSwssSyslogSink
    *
    *@brief add swss syslog sink to the logger
    *
    *@return none
    */
    void addSwssSyslogSink(std::string &prog);

private:
    typedef std::map<boost::log::sinks::syslog::level, boost::log::trivial::severity_level> BoostLogLevelMap;
    static const BoostLogLevelMap mBoostLogLevelMapper;

    typedef std::map<boost::log::trivial::severity_level, boost::log::sinks::syslog::level> SyslogLevelMap;
    static const SyslogLevelMap mSyslogLevelMapper;

private:
    bool mLinkToSwssLogger = false;
    boost::log::trivial::severity_level mLevel = boost::log::trivial::info;

    boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> mSeverityLogger;
};

} /* namespace common */

#define XSTR(x) STR(x)
#define STR(x) #x

/**
 * MUXLOG macro prepends file name, line number, and function to logged message
 *
 *@param level (in)  logging severity level
 *@param msg (in)    message to be logged
 */
#define MUXLOG(level, msg) \
    do { \
        if (level >= common::MuxLogger::getInstance()->getLevel()) { \
            BOOST_LOG_SEV(common::MuxLogger::getInstance()->getLogger(), level) \
                << XSTR(__FILENAME__) << ":" << __LINE__ << " " << __FUNCTION__ << ": " \
                << msg; \
        } \
  } while (0)

/**
 * MUXLOGTRACE handy macro that logs with trace severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGTRACE(msg)           MUXLOG(boost::log::trivial::trace, msg)

/**
 * MUXLOGDEBUG handy macro that logs with debug severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGDEBUG(msg)           MUXLOG(boost::log::trivial::debug, msg)

/**
 * MUXLOGINFO handy macro that logs with info severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGINFO(msg)            MUXLOG(boost::log::trivial::info, msg)

/**
 * MUXLOGWARNING handy macro that logs with warning severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGWARNING(msg)         MUXLOG(boost::log::trivial::warning, msg)

/**
 * MUXLOGERROR handy macro that logs with error severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGERROR(msg)           MUXLOG(boost::log::trivial::error, msg)

/**
 * MUXLOGFATAL handy macro that logs with fatal severity level and appends file
 * name, line number, function to the message being logged
 *
 *@param msg (in)    message to be logged
 */
#define MUXLOGFATAL(msg)           MUXLOG(boost::log::trivial::fatal, msg)

#endif /* MUXLOGGER_H_ */
